/**
 * 
 */
package org.dragonfire.dasm.directives;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import org.dragonfire.dasm.Command;
import org.dragonfire.dasm.Listing;
import org.dragonfire.dasm.DirectiveManager.Directive;
import org.dragonfire.parser.Equation;
import org.dragonfire.parser.functions.Function;

/**
 * Implements a queue data structure that allows for data to be stored in a FIFO
 * (first in, first out) manner. Queues may be named to maintain multiple
 * separate queues<br />
 * <br />
 * Format:<br />
 * <code>#QUEUE [<i>&lt;queueName&gt;</i>,]<i>&lt;data1&gt;</i>[,<i>&lt;data2&gt;</i>[,...]]</code><br />
 * <br />
 * Parameters:<br />
 * <table>
 * <tr>
 * <td><code>queueName</code></td>
 * <td>(String, Optional) The name of the queue in which to insert the data. If
 * this parameter is omitted, then {@link #DEFAULT_QUEUE} is used and <b>only
 * one data value is permitted</b>.</td>
 * </tr>
 * <tr>
 * <td><code>data1</code></td>
 * <td>The value to insert into the queue. If multiple data parameters are
 * passed, then <code>queueName</code> must also be passed.</td>
 * </tr>
 * </table><br />
 * Errors:<br />
 * <table>
 * <tr>
 * <td><code>ERROR_STRING_REQUIRED</code></td>
 * <td>Thrown if <code>queueName</code> is not a string</td>
 * </tr>
 * </table>
 * 
 * @author Darth Android
 * @version 2.0
 * @see Deque
 * @see java.util.Queue
 * @see java.util.LinkedList
 */
public class Queue extends Directive {

	/**
	 * The name of the queue to use when one isn't specified
	 */
	public static final String DEFAULT_QUEUE = "";
	/**
	 * Queue data
	 */
	private HashMap<String, LinkedList<String>> data = new HashMap<String, LinkedList<String>>();

	/**
	 * Default constructor registers the <code>$deque</code> function
	 */
	public Queue() {
		Function.putFunction("dequeue", new Deque());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ArrayList<Listing> pass1(Command c) {
		ArrayList<Listing> list = new ArrayList<Listing>();
		// start with the default queue name
		String queueName = DEFAULT_QUEUE;
		// loop through all params
		for (int i = 0; i < c.getParams().length; i++) {
			String param = c.getParam(i);
			// if more than one parameters, then the first is the queue name
			if (c.getParams().length > 1 && i == 0)
				queueName = evalString(param);
			else {
				// else push the parameter onto the queue
				list.add(new Listing(0, c));
				// create a new queue if one doesn't already exist
				if (!data.containsKey(queueName))
					data.put(queueName, new LinkedList<String>());
				data.get(queueName).add(param);
			}
		}
		return list;
	}

	/**
	 * Removes the head of a named queue
	 * 
	 * @param queueName
	 *            The name of the queue to retrieve data from
	 * @return The head of the queue
	 * @throws NoSuchElementException
	 *             If the queue is empty
	 * @throws IllegalArgumentException
	 *             If the queue doesn't exist (read: hasn't been created by
	 *             <code>#QUEUE</code> yet)
	 */
	public String getData(String queueName) {
		if (!data.containsKey(queueName))
			throw new IllegalArgumentException("No queue named '" + queueName + "'.");
		return data.get(queueName).remove();
	}

	/**
	 * Determines if a queue has at least one element
	 * 
	 * @param queueName
	 *            The name of the queue
	 * @return <code>true</code> if the queue is not empty<br />
	 *         <code>false</code> if the queue is empty
	 * @see #getData(String)
	 */
	public boolean hasData(String queueName) {
		if (data.containsKey(queueName) == false)
			return false;
		return !data.get(queueName).isEmpty();
	}

	/**
	 * Removes the head of the named queue (or default queue if a name is not
	 * given) and returns it. If the queue is empty, then a value of
	 * <code>0</code> is returned.<br />
	 * <br />
	 * Format:<br />
	 * <code>$deque([<i>&lt;queueName&gt;</i>])</code><br />
	 * <br />
	 * Parameters:<br />
	 * <table>
	 * <tr>
	 * <td><code>queueName</code></td>
	 * <td>(String, Optional) The name of the queue from which to remove the
	 * data. If this parameter is omitted, then {@link #DEFAULT_QUEUE} is used.</td>
	 * </tr>
	 * </table><br />
	 * <br />
	 * Errors:<br />
	 * <table>
	 * <tr>
	 * <td><code>ERROR_STRING_REQUIRED</code></td>
	 * <td>Thrown if <code>queueName</code> is not a string</td>
	 * </tr>
	 * </table>
	 * 
	 * @see Queue
	 * @version 2.0
	 * @author Darth Android
	 */
	public class Deque extends Function {

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int minParams() {
			return 0;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public String eval(Equation equ, String[] args) {
			String queueName = DEFAULT_QUEUE;
			if (args.length > 0)
				queueName = evalString(args[0]);
			// return the head if the queue isn't empty
			if (hasData(queueName))
				return getData(queueName);
			return "0";
		}
	}
}
